/**************************************************************************************************
	$Header: /pub/cvsroot/mydns/src/mydns/listen.c,v 1.4 2002/08/02 08:49:11 bboy Exp $
	listen.c: Routines to get listening addresses, and to bind to those addresses.

	Copyright (C) 2002  Don Moore <bboy@bboy.net>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at Your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
**************************************************************************************************/

#include "named.h"

int *tcp_fd, *udp_fd;											/* Listening sockets (TCP/UDP) */
int num_tcp_fd, num_udp_fd;									/* Number of listeners */


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	LISTENER
	Create server socket, return fd.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
static int
listener(char *ip, int port, int protocol)
{
	int fd, opt = 1;
	struct sockaddr_in sa;

	if ((fd = socket(AF_INET, protocol, (protocol == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP)) < 0)
		Err("socket");
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
		Warn("setsockopt");
	fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);

	memset(&sa, 0, sizeof(sa));
	sa.sin_family = AF_INET;
	sa.sin_port = htons(port);
	if (ip && *ip != '*')
	{
		if (!(inet_aton(ip, &sa.sin_addr)))
			Errx("%s: %s", ip, _("error interpreting IP address"));
	}
	else
		sa.sin_addr.s_addr = htonl(INADDR_ANY);

	if (bind(fd, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0)
		Err("bind (%s): %s", (protocol == SOCK_STREAM) ? "TCP" : "UDP", inet_ntoa(sa.sin_addr));
	if (protocol == SOCK_STREAM)
	{
		if (listen(fd, 5) < 0)
			Err("listen");
	}
	else
	{
		int n, size;
		for (n = 1; n < 1024; n++)
		{
			size = n * 1024;
			if ((setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size))) < 0)
				break;
		}
	}
	return (fd);
}
/*--- listener() --------------------------------------------------------------------------------*/


/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	CREATE_LISTENERS
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void
create_listeners(void)
{
	char *bind_opt = conf_get(&Conf, "listen"), *optcopy, *c, *ip;
	int port = atoi(conf_get(&Conf, "port"));

#if DEBUG
	Debug(_("creating listening sockets"));
#endif

	optcopy = xstrdup(bind_opt);
	for (c = optcopy; (ip = strsep(&c, ",")); )
	{
		if (!num_udp_fd)
			udp_fd = xmalloc(sizeof(int));
		else
			udp_fd = xrealloc(udp_fd, num_udp_fd * sizeof(int));
		udp_fd[num_udp_fd++] = listener(ip, port, SOCK_DGRAM);
		if (axfr_enabled || tcp_enabled)
		{
			if (!num_tcp_fd)
				tcp_fd = xmalloc(sizeof(int));
			else
				tcp_fd = xrealloc(tcp_fd, num_tcp_fd * sizeof(int));
			tcp_fd[num_tcp_fd++] = listener(ip, port, SOCK_STREAM);
		}
#if DEBUG
		Debug("%s: %s", ip, _("address added"));
#endif
	}
	free(optcopy);
	Notice("%s %s %s (port %d)", progname, PACKAGE_VERSION, _("ready"), port);
}
/*--- create_listeners() ------------------------------------------------------------------------*/

/* vi:set ts=3: */
/* NEED_PO */
